{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training with SageMaker Pipe Mode and TensorFlow using the SageMaker Python SDK\n",
    "\n",
    "SageMaker Pipe Mode is an input mechanism for SageMaker training containers based on Linux named pipes. SageMaker makes the data available to the training container using named pipes, which allows data to be downloaded from S3 to the container while training is running. For larger datasets, this dramatically improves the time to start training, as the data does not need to be first downloaded to the container. To learn more about pipe mode, please consult the AWS documentation at: https://docs.aws.amazon.com/sagemaker/latest/dg/your-algorithms-training-algo.html#your-algorithms-training-algo-running-container-trainingdata.\n",
    "\n",
    "In this tutorial, we show you how to train a tf.estimator using data read with SageMaker Pipe Mode. We'll use the SageMaker `PipeModeDataset` class - a special TensorFlow `Dataset` built specifically to read from SageMaker Pipe Mode data. This `Dataset` is available in our TensorFlow containers for TensorFlow versions 1.7.0 and up. It's also open-sourced at https://github.com/aws/sagemaker-tensorflow-extensions and can be built into custom TensorFlow images for use in SageMaker. \n",
    "\n",
    "Although you can also build the PipeModeDataset into your own containers, in this tutorial we'll show how you can use the PipeModeDataset by launching training from the SageMaker Python SDK. The SageMaker Python SDK helps you deploy your models for training and hosting in optimized, production ready containers in SageMaker. The SageMaker Python SDK is easy to use, modular, extensible and compatible with TensorFlow and MXNet. \n",
    "\n",
    "Different collections of S3 files can be made available to the training container while it's running. These are referred to as \"channels\" in SageMaker. In this example, we use two channels - one for training data and one for evaluation data. Each channel is mapped to S3 files from different directories. The SageMaker PipeModeDataset knows how to read from the named pipes for each channel given just the channel name. When we launch SageMaker training we tell SageMaker what channels we have and where in S3 to read the data for each channel.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "The following code snippet sets up some variables we'll need later on. Please provide an S3 bucket that a TensorFlow training script and training output can be stored in.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "isConfigCell": true
   },
   "outputs": [],
   "source": [
    "from sagemaker import get_execution_role\n",
    "from sagemaker.session import Session\n",
    "\n",
    "# S3 bucket for saving code and model artifacts.\n",
    "# Feel free to specify a different bucket here if you wish.\n",
    "bucket = Session().default_bucket()\n",
    "\n",
    "# Location to save your custom code in tar.gz format.\n",
    "custom_code_upload_location = 's3://{}/customcode/tensorflow_pipemode'.format(bucket)\n",
    "\n",
    "# Location where results of model training are saved.\n",
    "model_artifacts_location = 's3://{}/artifacts'.format(bucket)\n",
    "\n",
    "# IAM execution role that gives SageMaker access to resources in your AWS account.\n",
    "role = get_execution_role()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Complete training source code \n",
    "\n",
    "In this tutorial we train a TensorFlow LinearClassifier using pipe mode data. The TensorFlow training script is contained in following file:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cat \"pipemode.py\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The above script implements all the functions required for a sagemaker tensorflow training script (See: [Preparing TensorFlow Training Script](https://github.com/aws/sagemaker-python-sdk/blob/master/src/sagemaker/tensorflow/README.rst#preparing-the-tensorflow-training-script)). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Using a PipeModeDataset in an input_fn\n",
    "To train an estimator using a Pipe Mode channel, we must construct an input_fn that reads from the channel. To do this, we use the SageMaker PipeModeDataset. This is a TensorFlow Dataset specifically created to read from a SageMaker Pipe Mode channel. A PipeModeDataset is a fully-featured TensorFlow Dataset and can be used in exactly the same ways as a regular TensorFlow Dataset can be used.\n",
    "\n",
    "The training and evaluation data used in this tutorial is synthetic. It contains a series of records stored in a TensorFlow Example protobuf object. Each record contains a numeric class label and an array of 1024 floating point numbers. Each array is sampled from a multi-dimensional Gaussian distribution with a class-specific mean. This means it is possible to learn a model using a TensorFlow Linear classifier which can classify examples well. Each record is separated using RecordIO encoding (though the PipeModeDataset class also supports the TFRecord format as well). \n",
    "\n",
    "The training and evaluation data were produced using the benchmarking source code in the sagemaker-tensorflow-extensions benchmarking sub-package. If you want to investigate this further, please visit the GitHub repository for sagemaker-tensorflow-extensions at https://github.com/aws/sagemaker-tensorflow-extensions. \n",
    "\n",
    "The following example code shows how to use a PipeModeDataset in an input_fn.\n",
    "\n",
    "```python\n",
    "from sagemaker_tensorflow import PipeModeDataset\n",
    "\n",
    "def input_fn():\n",
    "    # Simple example data - a labeled vector.\n",
    "    features = {\n",
    "        'data': tf.FixedLenFeature([], tf.string),\n",
    "        'labels': tf.FixedLenFeature([], tf.int64),\n",
    "    }\n",
    "    \n",
    "    # A function to parse record bytes to a labeled vector record\n",
    "    def parse(record):\n",
    "        parsed = tf.parse_single_example(record, features)\n",
    "        return ({\n",
    "            'data': tf.decode_raw(parsed['data'], tf.float64)\n",
    "        }, parsed['labels'])\n",
    "\n",
    "    # Construct a PipeModeDataset reading from a 'training' channel, using\n",
    "    # the TF Record encoding.\n",
    "    ds = PipeModeDataset(channel='training', record_format='TFRecord')\n",
    "\n",
    "    # The PipeModeDataset is a TensorFlow Dataset and provides standard Dataset methods\n",
    "    ds = ds.repeat(20)\n",
    "    ds = ds.prefetch(10)\n",
    "    ds = ds.map(parse, num_parallel_calls=10)\n",
    "    ds = ds.batch(64)\n",
    "    \n",
    "    return ds\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Running training using the Python SDK\n",
    "\n",
    "We can use the SDK to run our local training script on SageMaker infrastructure.\n",
    "\n",
    "1. Pass the path to the pipemode.py file, which contains the functions for defining your estimator, to the sagemaker.TensorFlow init method.\n",
    "2. Pass the S3 location that we uploaded our data to previously to the fit() method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sagemaker.tensorflow import TensorFlow\n",
    "\n",
    "tensorflow = TensorFlow(entry_point='pipemode.py',\n",
    "                        role=role,\n",
    "                        framework_version='1.12.0',\n",
    "                        input_mode='Pipe',\n",
    "                        output_path=model_artifacts_location,\n",
    "                        code_location=custom_code_upload_location,\n",
    "                        train_instance_count=1,\n",
    "                        training_steps=1000,\n",
    "                        evaluation_steps=100,\n",
    "                        train_instance_type='ml.c4.xlarge')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After we've created the SageMaker Python SDK TensorFlow object, we can call fit to launch TensorFlow training:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "import boto3\n",
    "\n",
    "# use the region-specific sample data bucket\n",
    "region = boto3.Session().region_name\n",
    "\n",
    "train_data = 's3://sagemaker-sample-data-{}/tensorflow/pipe-mode/train'.format(region)\n",
    "eval_data = 's3://sagemaker-sample-data-{}/tensorflow/pipe-mode/eval'.format(region)\n",
    "\n",
    "tensorflow.fit({'train':train_data, 'eval':eval_data})\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After ``fit`` returns, you've successfully trained a TensorFlow LinearClassifier using SageMaker pipe mode! The TensorFlow model data will be stored in '``s3://<bucket-name>/artifacts``' - where '``<bucket-name>``' is the name of the bucket you supplied earlier."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "conda_tensorflow_p27",
   "language": "python",
   "name": "conda_tensorflow_p27"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.14"
  },
  "notice": "Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not use this file except in compliance with the License. A copy of the License is located at http://aws.amazon.com/apache2.0/ or in the \"license\" file accompanying this file. This file is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
